package com.windaka.elements.websocket.interceptor;

import com.windaka.elements.utils.CheckParamUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

import java.util.Map;

/**
 * WebSocketInterceptor 拦截器
 * 做了登录校验 token是否有效
 * @author song
 * @date 11:17 2022/8/22
**/
@Slf4j
@Component
@RequiredArgsConstructor
public class WebSocketInterceptor implements HandshakeInterceptor {

    /**
     * SEC_TOKEN_NAME
     */
    private static final String SEC_TOKEN_NAME = "Sec-WebSocket-Protocol";

    /**
     * TOKEN_NAME
     */
    private static final String TOKEN_NAME = "token";

    /**
     * 配置中的 权限前缀
     */
    @Value("${token-prefix}")
    private String tokenPrefix;

    /**
     * Redis 存储的前缀
     */
    private static final String REDIS_TOKEN_PREFIX = ":access:";

    /**
     * token的前缀
     */
    private static final String TOKEN_PREFIX = "Bearer ";

    /**
     * redis
     */
    private final RedisTemplate<String, Object> redisTemplate;

    /**
     * 执行连接前
     * @author song
     * @date 11:18 2022/8/22
    **/
    @Override
    public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) {
        ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) serverHttpRequest;
        ServletServerHttpResponse servletServerHttpResponse = (ServletServerHttpResponse) serverHttpResponse;
        // 获取用户传递的token
        String token = servletServerHttpRequest.getServletRequest().getHeader(SEC_TOKEN_NAME);
        // 校验 token 是否为空 是否有效
        if (Boolean.FALSE.equals(tokenIsActive(token))) {
            log.info("[WebSocketInterceptor => beforeHandshake Token校验失败, {}]", token);
            return false;
        }
        // 校验通过
        // 将 token 参数 携带至请求
        servletServerHttpResponse.getServletResponse().setHeader(SEC_TOKEN_NAME, token);
        map.put(TOKEN_NAME, token);
        log.info("[WebSocketInterceptor => beforeHandshake Token校验成功, {}]", token);
        return true;
    }

    /**
     * 执行连接后
     * @author song
     * @date 11:18 2022/8/22
    **/
    @Override
    public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
        log.info("[WebSocketInterceptor => afterHandshake 成功建立连接]");
    }

    /**
     * 校验token是否活着
     * @author song
     * @date 10:38 2022/8/20
     **/
    private Boolean tokenIsActive(String token) {
        if (CheckParamUtils.isNullOrEmpty(token)) {
            return false;
        }
        token = token.replace(TOKEN_PREFIX, "");
        return Boolean.TRUE.equals(redisTemplate.hasKey(tokenPrefix + REDIS_TOKEN_PREFIX + token));
    }
}
